package org.nemanjaristic.database.managers;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class HibernateEntityManager<T, ID extends Serializable> implements EntityManager<T, ID> {
	
	private static final Logger logger = LoggerFactory.getLogger(EntityManager.class);

	private Session session;
	private Class<T> entityClass;

	@SuppressWarnings("unchecked")
	public HibernateEntityManager() {
		this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}

	public T add(T entity) {
		Session session = getSession();
		
		try {
			session.saveOrUpdate(entity);
			
			logger.debug("Entity {} saved.", entity.getClass().getSimpleName());
		}
		catch (HibernateException e) {
			logger.warn("Can't save entity {}. {}", entity.getClass().getSimpleName(), e.getMessage());
		}

		return entity;
	};

	public void update(T entity) {
		Session session = getSession();
		
		try {
			session.saveOrUpdate(entity);
			
			logger.debug("Entity {} updated.", entity.getClass().getSimpleName());
		}
		catch (HibernateException e) {
			logger.warn("Can't update entity {}. {}", entity.getClass().getSimpleName(), e.getMessage());
		}
	};

	public void remove(T entity) {
		Session session = getSession();
		
		try {
			session.delete(entity);
			
			logger.debug("Entity {} removed.", entity.getClass().getSimpleName());
		}
		catch (HibernateException e) {
			logger.warn("Can't remove entity {}. {}", entity.getClass().getSimpleName(), e.getMessage());
		}
	};

	@SuppressWarnings("unchecked")
	public T get(ID id) {
		return (T) getSession().load(entityClass, id);
	};

	@Override
	public List<T> getAll() {
		List<T> results = null;

		try {
			results = findByCriteria();
		}
		catch (HibernateException e) {
			logger.error("Can't load all {} entities. {}", entityClass.getSimpleName(), e.getMessage());
		}

		return results;
	}
	
	public List<T> getAll(Criterion... criterion) {
		List<T> results = null;

		try {
			results = findByCriteria(criterion);
		}
		catch (HibernateException e) {
			logger.error("Can't load all {} entities. {}", entityClass.getSimpleName(), e.getMessage());
		}

		return results;
	}

	@SuppressWarnings("unchecked")
	protected List<T> findByCriteria(Criterion... criterion) {
		Criteria crit = getSession().createCriteria(entityClass);
		for (Criterion c : criterion)
			crit.add(c);

		return crit.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> findByExample(T exampleInstance, String[] excludeProperty) {
		Criteria crit = getSession().createCriteria(entityClass);
		Example example = Example.create(exampleInstance);
		for (String exclude : excludeProperty)
			example.excludeProperty(exclude);

		crit.add(example);

		return crit.list();
	}

	public Session getSession() {
		if (session == null)
			throw new IllegalStateException("Session has not been set on DAO before usage.");

		return session;
	}

	public void setSession(Session session) {
		this.session = session;
	}
}
